package pda.datas;

import checkers.Checkers;
import checkers.Move;
import java.awt.Point;
import java.io.File;
import utilities.FileManager;
import java.util.ArrayList;
import javax.swing.*;
import javax.swing.filechooser.FileFilter;
import pda.view.CheckersView;

class MyFilter extends FileFilter
{
    public boolean accept(File f)
    {
        if(f.isDirectory()) return true;
        String ext = getExtension(f);

        if(ext == null)
            return false;
        else if(ext.equals("csf"))
            return true;
        else
            return false;
    }

    public String getDescription()
    {
        return "Checkers Save File (*.csf)";
    }

    public static String getExtension(File f)
    {
        String ext = null;
        String s = f.getName();
        int i = s.lastIndexOf('.');

        if (i>0 && i<s.length()-1)
            ext = s.substring(i+1).toLowerCase();
        return ext;
    }
}

public class CheckersData implements Runnable
{
    private Object[] BOARD_SIZES;
    private JFileChooser fileChooser = new JFileChooser(".");
    
    public Checkers check;
    private CheckersView view;
    
    int depth = 10;
    public boolean positionEdit = false;
    public boolean holdAI = false;
    private boolean whiteIsAI = false;
    private boolean blackIsAI = true;
    public boolean quit = false;
    private boolean cancelAI = false;
    
    public CheckersData(int size)
    {
        check = new Checkers(size);
        fileChooser.setAcceptAllFileFilterUsed(false);
        fileChooser.setFileFilter(new MyFilter());
        BOARD_SIZES = new Object[9];
        
        int value = 4;
        for(int i=0; i<9; ++i)
        {
            BOARD_SIZES[i] = value;
            value += 2;
        }
    }
    
    public void setView(CheckersView view)
    {
        this.view = view;
    }
    
    public void newGame()
    {
        Integer size = null;
        size = (Integer) JOptionPane.showInputDialog(null, "Select the board size", "New game",
                JOptionPane.PLAIN_MESSAGE, null, BOARD_SIZES, BOARD_SIZES[3]);
        
        if(size != null)
        {
            stopAI();
            check = new Checkers(size);
            view.updateDrawSizes();
            updateLastMove();
            if(positionEdit) exitPosEdit();
            view.updateScreen();
            startAI();
        }
    }
    
    public void loadGame()
    {
        fileChooser.showOpenDialog(null);
        if(fileChooser.getSelectedFile() == null) return;
        
        Checkers c = (Checkers) FileManager.deserialize(fileChooser.getSelectedFile().getAbsolutePath());
        
        if(c != null)
        {
            stopAI();
            check = c;
            check.resetHashTable();
            view.updateDrawSizes();
            updateLastMove();
            if(positionEdit) exitPosEdit();
            view.updateScreen();
            if(!checkVictory()) startAI();
        }
        else
            JOptionPane.showMessageDialog(null, "Invalid or corrupted save file!", "Error", JOptionPane.ERROR_MESSAGE);
        
        fileChooser.setSelectedFile(null);
    }
    
    public void saveGame()
    {
        fileChooser.showSaveDialog(null);
        if(fileChooser.getSelectedFile() != null)
        {
            stopAI();
            
            String name = fileChooser.getSelectedFile().getAbsolutePath();
            String ext = MyFilter.getExtension(fileChooser.getSelectedFile());
            
            if(ext == null || !ext.toLowerCase().equals("csf"))
                name += ".csf";
            
            FileManager.serialize(check, name);
            fileChooser.setSelectedFile(null);
            
            startAI();
        }
    }
    
    public void updateLastMove()
    {
        view.validSquares.clear();
        int lastInd = -1;
        
        for(Move m : check.validMoves)
        {
            if(m.start != lastInd)
                view.validSquares.add(m.start);
            lastInd = m.start;
        }
        
        if(check.currentMove == 0)
            view.updateLastMove(null, null);
        else
        {
            Move last = check.getLastMove();
            view.updateLastMove(last.getSquares(), last.getCapSquares());
        }
    }
    
    public void setDrag(Point p)
    {
        if(isAiTurn() || check.getStatus()>0) return;
        synchronized(this){};
        
        int square = view.coordToSquare(p.x, p.y);
        if(square == -1) return;

        view.dragSquare = square;
        view.destSquare = -1;
        view.dragPiece = check.getSquare(square);
        view.dragX = p.x;
        view.dragY = p.y;
        
        view.updateScreen();
    }
    
    public void play(int x, int y)
    {
        int square = view.coordToSquare(x, y);
        if(square == -1) {view.dragSquare = -1; return;}
        
        ArrayList<Move> moves = check.getPlayerMove(view.dragSquare, square);
        Move m;
        
        if(moves.isEmpty())
            m = null;
        else if(moves.size()==1)
            m = moves.get(0);
        else
        {
            boolean oldValue = view.showNumbers;
            view.showNumbers = true;
            view.updateScreen();
            m = (Move) JOptionPane.showInputDialog(null, "Choose your move", "Move ambiguity", JOptionPane.PLAIN_MESSAGE,
                    null, moves.toArray(), moves.get(0));
            view.showNumbers = oldValue;
            
        }
        
        if(m != null)
        {
            check.play(m);
            updateLastMove();
            view.dragSquare = -1;
            view.updateScreen();
            if(!checkVictory()) startAI();
        }
        else
            view.dragSquare = -1;
    }
    
    public void previousMove()
    {
        if(check.currentMove == 0) return;
        stopAI();
        check.previousMove();
        updateLastMove();
        view.updateScreen();
        startAI();
    }
    
    public void nextMove()
    {
        if(check.currentMove == check.movesPlayed.size()) return;
        stopAI();
        check.nextMove();
        updateLastMove();
        view.updateScreen();
        startAI();        
    }
    
    public void toggleWhite()
    {
        if(positionEdit || !check.whiteToPlay())
        {
            whiteIsAI ^= true;
        }
        else if(check.whiteToPlay() && whiteIsAI)
        {
            whiteIsAI = false;
            stopAI();
            view.updateScreen();
        }
        else if(check.whiteToPlay() && !whiteIsAI)
        {
            whiteIsAI = true;
            startAI();
            view.updateScreen();
        }
    }
    
    public void toggleBlack()
    {
        if(positionEdit || check.whiteToPlay())
        {
            blackIsAI ^= true;
        }
        else if(!check.whiteToPlay() && blackIsAI)
        {
            blackIsAI = false;
            stopAI();
            view.updateScreen();
        }
        else if(!check.whiteToPlay() && !blackIsAI)
        {
            blackIsAI = true;
            startAI();
            view.updateScreen();
        }
    }
    
    public void toggleTurn()
    {
        check.switchTurn();
        view.updateScreen();
    }
    
    public void togglePositionEdit()
    {
        if(positionEdit)
        {
            positionEdit = false;
            check.updateStatus();
            if(!checkVictory()) startAI();
            view.dragSquare = -1;
            view.emptyBoard.setEnabled(false);
            view.saveGame.setEnabled(true);
            updateLastMove();
            view.updateScreen();
        }
        else
        {
            stopAI();
            view.emptyBoard.setEnabled(true);
            view.saveGame.setEnabled(false);
            positionEdit = true;
            check.currentMove = 0;
            check.movesPlayed.clear();
            check.validMoves.clear();
            view.dragPiece = Checkers.WHITE_PAWN;
            view.dragSquare = 0;
            updateLastMove();
            view.updateScreen();
        }
    }
    
    public void exitPosEdit()
    {
        positionEdit = false;
        view.editPosCheck.setSelected(false);
        view.emptyBoard.setEnabled(false);
        view.saveGame.setEnabled(true);
        view.dragSquare = -1;
    }
    
    public void emptyBoard()
    {
        for(int i=1; i<=check.squaresNum; ++i)
            check.setSquare(i, -1);
    }
    
    public void setDepth(int depth)
    {
        this.depth = depth;
    }
    
    public synchronized void startAI()
    {
        holdAI = false;
        cancelAI = false;
        notifyAll();
    }
    
    public void stopAI()
    {
        holdAI = true;
        cancelAI = true;
        check.setAiLimit(20);
        synchronized(this){};
    }
    
    public void moveNow()
    {
        check.setAiLimit(20);
    }
    
    public boolean isAiTurn()
    {
        if(check.whiteToPlay())
        {
            if(whiteIsAI) return true;
        }
        else
            if(blackIsAI) return true;
        
        return false;
    }
    
    public boolean checkVictory()
    {
        if(check.getStatus() == Checkers.WHITE_WIN)
        {
            JOptionPane.showMessageDialog(null, "White wins");
            return true;
        }
        else if(check.getStatus() == Checkers.BLACK_WIN)
        {
            JOptionPane.showMessageDialog(null, "Black wins");
            return true;
        }
        else if(check.getStatus() == Checkers.DRAW)
        {
            JOptionPane.showMessageDialog(null, "Draw by 25 move rule");
            return true;
        }
        else
            return false;
    }
    
    public synchronized void playAI()
    {
        while(true)
        {
            try
            {
                while(!isAiTurn() || holdAI || check.getStatus()>0)
                {
                    view.moveNow.setEnabled(false);
                    wait();
                    if(quit) break;
                }
            }
            catch(InterruptedException e){System.out.println(e);}
            
            if(quit) break;
            check.setAiLimit(0);
            view.moveNow.setEnabled(true);
            
            long time = System.currentTimeMillis();
            Move move = check.getBestMoveAI(depth);
            System.out.println(System.currentTimeMillis()-time);
            
            if(!cancelAI)
            {
                check.play(move);
                updateLastMove();
                view.updateScreen();
                checkVictory();
            }
        }
    }

    @Override
    public void run()
    {
        playAI();
    }
}
